/*
 * Copyright (c) 2024, sakumisu
 *
 * SPDX-License-Identifier: Apache-2.0
 */
#include "usbd_core.h"
#include "usbd_msc.h"
#include "bootuf2.h"

#define MSC_IN_EP  0x81
#define MSC_OUT_EP 0x02

#define USB_CONFIG_SIZE (9 + MSC_DESCRIPTOR_LEN)

static const uint8_t device_descriptor[] = {
    USB_DEVICE_DESCRIPTOR_INIT(USB_2_0, 0x00, 0x00, 0x00, USBD_VID, USBD_PID, 0x0200, 0x01),
};

static const uint8_t config_descriptor_hs[] = {
    USB_CONFIG_DESCRIPTOR_INIT(USB_CONFIG_SIZE, 0x01, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER),
    MSC_DESCRIPTOR_INIT(0x00, MSC_OUT_EP, MSC_IN_EP, USB_BULK_EP_MPS_HS, 0x02),
};

static const uint8_t config_descriptor_fs[] = {
    USB_CONFIG_DESCRIPTOR_INIT(USB_CONFIG_SIZE, 0x01, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER),
    MSC_DESCRIPTOR_INIT(0x00, MSC_OUT_EP, MSC_IN_EP, USB_BULK_EP_MPS_FS, 0x02),
};

static const uint8_t device_quality_descriptor[] = {
    USB_DEVICE_QUALIFIER_DESCRIPTOR_INIT(USB_2_0, 0x00, 0x00, 0x00, 0x01),
};

static const uint8_t other_speed_config_descriptor_hs[] = {
    USB_OTHER_SPEED_CONFIG_DESCRIPTOR_INIT(USB_CONFIG_SIZE, 0x01, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER),
    MSC_DESCRIPTOR_INIT(0x00, MSC_OUT_EP, MSC_IN_EP, USB_BULK_EP_MPS_FS, 0x02),
};

static const uint8_t other_speed_config_descriptor_fs[] = {
    USB_OTHER_SPEED_CONFIG_DESCRIPTOR_INIT(USB_CONFIG_SIZE, 0x01, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER),
    MSC_DESCRIPTOR_INIT(0x00, MSC_OUT_EP, MSC_IN_EP, USB_BULK_EP_MPS_HS, 0x02),
};

static const char *string_descriptors[] = {
    (const char[]){ 0x09, 0x04 }, /* Langid */
    "HPMicro",                    /* Manufacturer */
    "HPMicro uf2 DEMO",           /* Product */
    "2025022401",                 /* Serial Number */
};

static const uint8_t *device_descriptor_callback(uint8_t speed)
{
    (void)speed;

    return device_descriptor;
}

static const uint8_t *config_descriptor_callback(uint8_t speed)
{
    if (speed == USB_SPEED_HIGH) {
        return config_descriptor_hs;
    } else if (speed == USB_SPEED_FULL) {
        return config_descriptor_fs;
    } else {
        return NULL;
    }
}

static const uint8_t *device_quality_descriptor_callback(uint8_t speed)
{
    (void)speed;

    return device_quality_descriptor;
}

static const uint8_t *other_speed_config_descriptor_callback(uint8_t speed)
{
    if (speed == USB_SPEED_HIGH) {
        return other_speed_config_descriptor_hs;
    } else if (speed == USB_SPEED_FULL) {
        return other_speed_config_descriptor_fs;
    } else {
        return NULL;
    }
}

static const char *string_descriptor_callback(uint8_t speed, uint8_t index)
{
    (void)speed;

    if (index >= (sizeof(string_descriptors) / sizeof(char *))) {
        return NULL;
    }
    return string_descriptors[index];
}

const struct usb_descriptor msc_bootuf2_descriptor = {
    .device_descriptor_callback = device_descriptor_callback,
    .config_descriptor_callback = config_descriptor_callback,
    .device_quality_descriptor_callback = device_quality_descriptor_callback,
    .other_speed_descriptor_callback = other_speed_config_descriptor_callback,
    .string_descriptor_callback = string_descriptor_callback,
};

static void usbd_event_handler(uint8_t busid, uint8_t event)
{
    switch (event) {
        case USBD_EVENT_RESET:
            break;
        case USBD_EVENT_CONNECTED:
            break;
        case USBD_EVENT_DISCONNECTED:
            break;
        case USBD_EVENT_RESUME:
            break;
        case USBD_EVENT_SUSPEND:
            break;
        case USBD_EVENT_CONFIGURED:
            bootuf2_init();
            break;
        case USBD_EVENT_SET_REMOTE_WAKEUP:
            break;
        case USBD_EVENT_CLR_REMOTE_WAKEUP:
            break;

        default:
            break;
    }
}

void usbd_msc_get_cap(uint8_t busid, uint8_t lun, uint32_t *block_num, uint32_t *block_size)
{
    *block_num = bootuf2_get_sector_count();
    *block_size = bootuf2_get_sector_size();

    USB_LOG_INFO("sector count:%d, sector size:%d\n", *block_num, *block_size);
}
int usbd_msc_sector_read(uint8_t busid, uint8_t lun, uint32_t sector, uint8_t *buffer, uint32_t length)
{
    boot2uf2_read_sector(sector, buffer, length / bootuf2_get_sector_size());
    return 0;
}

int usbd_msc_sector_write(uint8_t busid, uint8_t lun, uint32_t sector, uint8_t *buffer, uint32_t length)
{
    bootuf2_write_sector(sector, buffer, length / bootuf2_get_sector_size());
    return 0;
}

static struct usbd_interface intf0;

void msc_bootuf2_init(uint8_t busid, uintptr_t reg_base)
{
    boot2uf2_flash_init();
    usbd_desc_register(busid, &msc_bootuf2_descriptor);
    usbd_add_interface(busid, usbd_msc_init_intf(busid, &intf0, MSC_OUT_EP, MSC_IN_EP));

    usbd_initialize(busid, reg_base, usbd_event_handler);
}

//
#include "board.h"
#include "hpm_romapi.h"
#include "hpm_l1c_drv.h"

//
#include "jtag.h"
#include "riscv_debug.h"
#include "target.h"
#include "flash/flash.h"

// #include "led_demo_flash.h" 
// #include "led_demo_ram.h"

target_t target_riscv;
//

uint32_t done_flag = 0;
uint32_t done_cnt = 10;

static xpi_nor_config_t s_xpi_nor_config;

void boot2uf2_flash_init(void)
{
    // xpi_nor_config_option_t option;
    // option.header.U = BOARD_APP_XPI_NOR_CFG_OPT_HDR;
    // option.option0.U = BOARD_APP_XPI_NOR_CFG_OPT_OPT0;
    // option.option1.U = BOARD_APP_XPI_NOR_CFG_OPT_OPT1;
    // rom_xpi_nor_auto_config(BOARD_APP_XPI_NOR_XPI_BASE, &s_xpi_nor_config, &option);
#if 1
    target_riscv_init(&target_riscv);
    target_riscv.init();
    if (irlen != 5)
    {
        RISCV_DEBUG_LOG_DBG("ERR: irlen = 0x%x \n", irlen);
        return;
    }
    if (tapnum != 1)
    {
        RISCV_DEBUG_LOG_DBG("ERR: tapnum = 0x%x \n", tapnum);
        return;
    }
    if (idcode != 0x1000563D)
    {
        RISCV_DEBUG_LOG_DBG("ERR: idcode = 0x%x \n", idcode);
        return;
    }
    target_riscv.idcode = idcode;

    target_riscv.reset(1);
    // target_riscv.resume(NULL, 0);
    target_riscv.halt();

    riscv_csr_dcsr_t dcsr;
    target_riscv.read_reg(&dcsr.reg, CSR_DCSR, 4);
    dcsr.ebreakm = 1;
    dcsr.ebreaks = 1;
    dcsr.ebreaku = 1;
    target_riscv.write_reg(&dcsr.reg, CSR_DCSR, 4);
     
    // const uint32_t option[4] = {0xfcf90002, 0x00000005, 0x1000, 0x0};
    flash_init();
    flash_erase(0, 0x10000);
    // flash_program(0x80000400 - 0x80000000, (uint8_t *)option, sizeof(option));
    // target_riscv.reset(0);
#endif
}

int bootuf2_flash_write(uint32_t address, const uint8_t *data, size_t size)
{
    USB_LOG_INFO("address:%08x, size:%d\n", address, size);
    done_cnt = 3; // reload reset cnt

    uint32_t const page_addr = address & ~(CONFIG_BOOTUF2_SECTOR_SIZE - 1);
    uint32_t const sector_addr = (page_addr - BOARD_FLASH_BASE_ADDRESS);

    // flash_erase(sector_addr, size);
    USB_LOG_INFO("Erase %08x success\n", sector_addr);

    flash_program(sector_addr, data, size);
    USB_LOG_INFO("Write %08x success\n", sector_addr);

#if 0
    USB_LOG_INFO("address:%08x, size:%d\n", address, size);
    hpm_stat_t status;
    done_cnt = 3;

    uint32_t const page_addr = address & ~(CONFIG_BOOTUF2_SECTOR_SIZE - 1);
    uint32_t const sector_addr = (page_addr - BOARD_FLASH_BASE_ADDRESS);
    disable_global_irq(CSR_MSTATUS_MIE_MASK);
    status = rom_xpi_nor_erase(BOARD_APP_XPI_NOR_XPI_BASE, xpi_xfer_channel_auto, &s_xpi_nor_config,
                               sector_addr, size);
    enable_global_irq(CSR_MSTATUS_MIE_MASK);

    if (status != status_success)
    {
        USB_LOG_ERR("Erase failed: status = %ld!\r\n", status);
        return 1;
    }

    USB_LOG_INFO("Erase %08x success\n", sector_addr);

    disable_global_irq(CSR_MSTATUS_MIE_MASK);
    status = rom_xpi_nor_program(BOARD_APP_XPI_NOR_XPI_BASE, xpi_xfer_channel_auto, &s_xpi_nor_config,
                                 (uint32_t *)data, sector_addr, size);
    enable_global_irq(CSR_MSTATUS_MIE_MASK);
    if (status != status_success)
    {
        USB_LOG_ERR("Page program failed: status = %ld!\r\n", status);
        return 1;
    }

    USB_LOG_INFO("Write %08x success\n", sector_addr);
#endif

    return 0;
}